从 v8.2.0 版本开始, FreeRTOS 新增了任务通知(Task Notifictions)这个功能,可以使用任务通知来代替信号量、消息队列、事件标志组等这些东西。使用任务通知的话效率会更高,本章我们就来学习一下 FreeRTOS 的任务通知功能,本章分为如下几部分:

  • 任务通知简介
  • 发送任务通知
  • 任务通知通用发送函数
  • 获取任务通知
  • 任务通知模拟二值信号量实验
  • 任务通知模拟计数型信号量实验
  • 任务通知模拟消息邮箱实验

任务通知简介

任务通知在 FreeRTOS 中是一个可选的功能,要使用任务通知的话就需要将宏configUSE_TASK_NOTIFICATIONS 定义为 1。FreeRTOS 的每个任务都有一个 32 位的通知值,任务控制块中的成员变量 ulNotifiedValue就是这个通知值。任务通知是一个事件,假如某个任务通知的接收任务因为等待任务通知而阻塞的话,向这个接收任务发送任务通知以后就会解除这个任务的阻塞状态。也可以更新接收任务的任务通知值,任务通知可以通过如下方法更新接收任务的通知值:

  • 不覆盖接收任务的通知值(如果上次发送给接收任务的通知还没被处理)。
  • 覆盖接收任务的通知值。
  • 更新接收任务通知值的一个或多个 bit。
  • 增加接收任务的通知值。

合理、灵活的使用上面这些更改任务通知值的方法可以在一些场合中替代队列、二值信号量、计数型信号量和事件标志组。使用任务通知来实现二值信号量功能的时候,解除任务阻塞的时间比直接使用二值信号量要快 45%(FreeRTOS 官方测试结果,使用 v8.1.2 版本中的二值信号量, GCC 编译器, -O2 优化的条件下测试的,没有使能断言函数 configASSERT()),并且使用的 RAM 更少。

任务通知的发送使用函数 xTaskNotify()或者 xTaskNotifyGive()(还有此函数的中断版本)来完成 ,这个通知值会一直 被保存着,直到接收任务调用函数 xTaskNotifyWait() 或 者ulTaskNotifyTake()来获取这个通知值。假如接收任务因为等待任务通知而阻塞的话那么在接收到任务通知以后就会解除阻塞态。

任务通知虽然可以提高速度,并且减少 RAM 的使用,但是任务通知也是有使用限制的:

  • FreeRTOS 的任务通知只能有一个接收任务,其实大多数的应用都是这种情况。
  • 接收任务可以因为接收任务通知而进入阻塞态,但是发送任务不会因为任务通知发送失败而阻塞。

发送任务通知

任务通知发送函数有 6 个:

函数 描述
xTaskNotify() 发送通知,带有通知值并且不保留接收任务原通知值,用在任务中。
xTaskNotifyFromISR() 发送通知,函数 xTaskNotify()的中断版本。
xTaskNotifyGive() 发送通知,不带通知值并且不保留接收任务的通知值,此函数会将接收任务的通知值加一,用于任务中。
vTaskNotifyGiveFromISR() 发送通知,函数 xTaskNotifyGive()的中断版本。
xTaskNotifyAndQuery() 发送通知,带有通知值并且保留接收任务的原通知值,用在任务中。
xTaskNotiryAndQueryFromISR() 发送通知,函数 xTaskNotifyAndQuery()的中断版本,用在中断服务函数中。

任务通知通用发送函数

任务级任务通知通用发送函数

小节中我们学习了 3 个任务级任务通知发送函数: xTaskNotify()、 xTaskNotifyGive()和 xTaskNotifyAndQuery(),这三个函数最终调用的都是函数 xTaskGenericNotify( )

中断级任务通知发送函数

中断级任务通知发送函数也有三个 ,分别为:xTaskNotifyFromISR() 、xTaskNotifyAndQueryFromISR()和 vTaskNotifyGiveFromISR()。其中函数 xTaskNotifyFromISR()和 xTaskNotifyAndQueryFromISR()最终调用的都是函数 xTaskGenericNotifyFromISR()。

获取任务通知

获取任务通知的函数有两个

函数 描述
ulTaskNotifyTake() 获取任务通知,可以设置在退出此函数的时候将任务通知值清零或者减一。当任务通知用作二值信号量或者计数信号量的时候使用此函数来获取信号量。
xTaskNotifyWait() 等待任务通知,比 ulTaskNotifyTak()更为强大,全功能版任务通知获取函数。